package com.suxin.spring.configuration;

import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.env.PropertySources;

import java.util.function.Consumer;

/**
 * @author Tang
 * @classname ConfigurationPropertiesBinder
 * @description [  ]
 * @date 2023/3/18 17:26
 */
public class ConfigurationPropertiesBinder {

    private static final Class<?> BEAN_CLASS = ConfigurationPropertiesBinder.class;
    private static final String BEAN_CLASS_NAME = BEAN_CLASS.getName();

    private final ApplicationContext applicationContext;
    private final PropertySources propertySources;

    public ConfigurationPropertiesBinder(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.propertySources = new PropertySourcesDeducer(applicationContext).getPropertySources();
    }

    public void bind(ConfigurationPropertiesBean propertiesBean) {
        final Bindable<?> bindTarget = propertiesBean.getBindTarget();
        // 绑定值到对象上
        getBinder().bind(propertiesBean.getName(), bindTarget);
    }

    public Binder getBinder() {
        return new Binder(getConfigurationPropertySource(),getPlaceholdersResolver(),
                getConversionService(),getPropertyEditorInitializer());
    }

    private Iterable<ConfigurationPropertySource> getConfigurationPropertySource() {
        return ConfigurationPropertySources.from(this.propertySources);
    }

    private PlaceholdersResolver getPlaceholdersResolver() {
        return new PropertySourcesPlaceholdersResolver(this.propertySources);
    }

    private ConversionService getConversionService(){
        return new ConversionServiceDeducer(this.applicationContext).getConversionService();
    }

    private Consumer<PropertyEditorRegistry> getPropertyEditorInitializer() {
        if (this.applicationContext instanceof ConfigurableApplicationContext) {
            return ((ConfigurableApplicationContext) this.applicationContext).getBeanFactory()::copyRegisteredEditorsTo;
        }
        return null;
    }

    public static void register(BeanDefinitionRegistry registry) {
        final GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(BEAN_CLASS);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(BEAN_CLASS_NAME, beanDefinition);
    }

    public static ConfigurationPropertiesBinder get(ApplicationContext applicationContext) {
        return (ConfigurationPropertiesBinder) applicationContext.getBean(BEAN_CLASS);
    }
}
